package com.chenyun.idemlimit.aspect;

import cn.hutool.extra.servlet.ServletUtil;
import cn.hutool.extra.spring.SpringUtil;
import com.chenyun.common.context.ServletContext;
import com.chenyun.common.exceptions.BusinessException;
import com.chenyun.common.utils.SpelUtil;
import com.chenyun.idemlimit.annotation.Limiter;
import lombok.extern.slf4j.Slf4j;
import org.aspectj.lang.JoinPoint;
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.annotation.Before;
import org.redisson.api.RRateLimiter;
import org.redisson.api.RateIntervalUnit;
import org.redisson.api.RateType;
import org.redisson.api.RedissonClient;

import java.io.Serializable;
import java.util.concurrent.TimeUnit;

/**
 * LimiterAspect：
 * Description:
 *
 * @author: Lonni
 * @date: 2024/1/19 0019 16:54
 */
@Slf4j
@Aspect
public class LimiterAspect implements Serializable {
    String RATE_LIMIT_KEY = "rate_limit:";
    /**
     * 方法之前
     * @param point
     * @param limiter
     * @throws Throwable
     */
    @Before("@annotation(limiter)")
    public void before(JoinPoint point, Limiter limiter) throws  Throwable{
        int time = limiter.time();
        int number = limiter.number();
        String msg = limiter.msg();
        String key = limiter.key();
        //解析key
        String newKey = SpelUtil.me.converSpEl(key, point);
        StringBuilder stringBuffer = new StringBuilder(RATE_LIMIT_KEY);

        RedissonClient redissonClient = SpringUtil.getBean(RedissonClient.class);
        stringBuffer.append(ServletContext.getRequest().getRequestURI()).append(":");
        stringBuffer.append(ServletUtil.getClientIPByHeader(ServletContext.getRequest())).append(":");
        stringBuffer.append(key).toString();


        try {
            RateType rateType = RateType.OVERALL;
//            if (rateLimiter.limitType() == LimitType.CLUSTER) {
//                rateType = RateType.PER_CLIENT;
//            }
            RRateLimiter rateLimiter = redissonClient.getRateLimiter(key);
            rateLimiter.trySetRate(rateType, number, time, RateIntervalUnit.SECONDS);
            long rnum=-1;
            // 5秒获取不到 直接返回
            if (!rateLimiter.tryAcquire(5, TimeUnit.SECONDS)) {
                return;
            }
            rnum= rateLimiter.availablePermits();
            if (rnum == -1) {
                throw new BusinessException(msg);
            }
            log.info("限制令牌 => {}, 剩余令牌 => {}, 缓存key => '{}'", number, rnum, newKey);
        } catch (Exception e) {
            if (e instanceof BusinessException) {
                throw e;
            } else {
                throw new RuntimeException("服务器限流异常，请稍候再试");
            }
        }


    }





}



